#include <atomic>
#include <cstdint>
#include <exception>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <string>
#include <system_error>
#include <thread>
#include <vector>

#include "cfgparser.h"
#include "cfgparser_storage_path.h"

namespace fs = std::filesystem;

namespace
{
    void print_section(const std::string &title)
    {
        std::cout << "\n==== " << title << " ====\n";
    }

    bool run_thread_safety_smoke(const fs::path &config_path)
    {
        cfgparser::CFGParser parser(config_path.string());

        parser.set("counter", 0);
        const std::vector<uint8_t> initial_payload{0xCA, 0xFE, 0xBA, 0xBE};
        parser.set("payload", initial_payload);
        if (!parser.save())
        {
            std::cerr << "Initial save for thread safety test failed at " << config_path << std::endl;
            return false;
        }

        constexpr std::size_t iterations = 20;
        constexpr std::size_t writer_iterations = iterations * 3;
        std::atomic<bool> failure{false};

        auto writer = std::thread([&]()
                                  {
            for (std::size_t i = 0; i < writer_iterations && !failure.load(); ++i)
            {
                parser.set("counter", static_cast<int>(i));
                std::vector<uint8_t> blob((i % 5) + 1, static_cast<uint8_t>(i & 0xFF));
                parser.set("blob", blob);

                if (i % 4 == 0)
                {
                    parser.remove("payload");
                }
                else
                {
                    parser.set("payload", blob);
                }
            } });

        auto reader = std::thread([&]()
                                  {
            for (std::size_t i = 0; i < writer_iterations && !failure.load(); ++i)
            {
                (void)parser.contains("counter");
                (void)parser.get("counter", -1);
                auto copy = parser.get("payload", std::vector<uint8_t>{});
                if (copy.size() > 4096)
                {
                    failure = true;
                    break;
                }
            } });

        auto saver = std::thread([&]()
                                 {
            for (std::size_t i = 0; i < iterations && !failure.load(); ++i)
            {
                if (!parser.save())
                {
                    failure = true;
                    break;
                }
            } });

        auto loader = std::thread([&]()
                                  {
            for (std::size_t i = 0; i < iterations && !failure.load(); ++i)
            {
                if (!parser.load())
                {
                    failure = true;
                    break;
                }
            } });

        auto snapshotter = std::thread([&]()
                                       {
            for (std::size_t i = 0; i < iterations && !failure.load(); ++i)
            {
                auto serialized = parser.serialize();
                if (serialized.empty() || parser.serialize_data_size() == 0)
                {
                    failure = true;
                    break;
                }
            } });

        writer.join();
        reader.join();
        saver.join();
        loader.join();
        snapshotter.join();

        if (failure.load())
        {
            std::cerr << "Thread safety smoke test detected a failure." << std::endl;
            return false;
        }

        std::cout << "Thread safety smoke test final counter="
                  << parser.get("counter", -1) << std::endl;
        return true;
    }

    int run_demo()
    {
        const fs::path demo_root = fs::temp_directory_path() / "cfgparser_demo";
        const fs::path config_path = demo_root / "config.bin";
        const fs::path backup_path = demo_root / "config_backup.bin";
        const fs::path concurrent_path = demo_root / "config_concurrency.bin";
        const fs::path autosave_path = demo_root / "config_autosave.bin";

        if (!cfgparser::create_directory(demo_root.string()))
        {
            std::cerr << "Failed to create demo directory at " << demo_root << std::endl;
            return 1;
        }

        cfgparser::CFGParser parser(config_path.string());

        print_section("Set values and save");
        parser.set("username", "alice");
        parser.set("volume", static_cast<int>(75));
        std::vector<uint8_t> calibration = {0xDE, 0xAD, 0xBE, 0xEF};
        parser.set("calibration", calibration.data(), calibration.size());

        std::cout << "serialize_data_size (payload only)=" << parser.serialize_data_size() << " bytes" << std::endl;
        const auto serialized_preview = parser.serialize();
        std::cout << "Serialized preview size=" << serialized_preview.size() << " bytes" << std::endl;

        cfgparser::CFGParser preview_parser(config_path.string());
        if (!preview_parser.deserialize(serialized_preview.data(), serialized_preview.size()))
        {
            std::cerr << "Preview round-trip deserialize failed" << std::endl;
            return 1;
        }
        std::cout << "Preview deserialize succeeded" << std::endl;

        if (!parser.save())
        {
            std::cerr << "Failed to save configuration to " << config_path << std::endl;
            return 1;
        }
        std::cout << "Saved configuration to " << config_path << std::endl;

        print_section("Thread safety smoke test");
        if (!run_thread_safety_smoke(concurrent_path))
        {
            return 1;
        }

        print_section("Auto-save on destruction");
        {
            cfgparser::CFGParserOptions autosave_opts;
            autosave_opts.auto_save_on_destruction = true;

            {
                cfgparser::CFGParser autosave_parser(autosave_path.string(), autosave_opts);
                autosave_parser.set("session_token", std::string("demo-token"));
                autosave_parser.set("session_counter", 42);
            }

            cfgparser::CFGParser verify_loader(autosave_path.string());
            if (!verify_loader.load())
            {
                std::cerr << "Auto-save verification failed to load " << autosave_path << std::endl;
                return 1;
            }

            std::cout << "session_token=" << verify_loader.get("session_token", std::string("<missing>"))
                      << ", session_counter=" << verify_loader.get("session_counter", -1) << std::endl;
        }

        print_section("Auto-load on construction");
        {
            cfgparser::CFGParserOptions autoload_opts;
            autoload_opts.auto_load_on_construction = true;
            cfgparser::CFGParser autoload_parser(autosave_path.string(), autoload_opts);
            std::cout << "auto-loaded session_token="
                      << autoload_parser.get("session_token", std::string("<missing>")) << std::endl;
        }

        print_section("Load values and verify");
        cfgparser::CFGParser loader(config_path.string());
        if (!loader.load())
        {
            std::cerr << "Failed to load configuration from " << config_path << std::endl;
            return 1;
        }

        const std::string default_username = "<missing>";
        std::cout << "username=" << loader.get("username", default_username) << std::endl;
        std::cout << "volume=" << loader.get("volume", 0) << std::endl;
        auto calibration_loaded = loader.get("calibration", std::vector<uint8_t>{});
        std::cout << "calibration=";
        std::cout << std::hex << std::setfill('0');
        for (auto byte : calibration_loaded)
        {
            std::cout << "0x" << std::setw(2) << static_cast<int>(byte) << ' ';
        }
        std::cout << std::dec << std::setfill(' ') << std::endl;

        print_section("Modify, remove, and save_as");
        loader.set("theme", "dark");
        loader.remove("calibration");
        if (!loader.save_as(backup_path.string()))
        {
            std::cerr << "Failed to save configuration to backup " << backup_path << std::endl;
            return 1;
        }
        std::cout << "Saved backup configuration to " << backup_path << std::endl;

        print_section("Reload backup and inspect");
        cfgparser::CFGParser backup(backup_path.string());
        if (!backup.load())
        {
            std::cerr << "Failed to load backup configuration from " << backup_path << std::endl;
            return 1;
        }

        std::cout << "Contains calibration? " << std::boolalpha
                  << backup.contains("calibration") << std::endl;
        const std::string default_theme = "<missing>";
        std::cout << "theme=" << backup.get("theme", default_theme) << std::endl;

        print_section("Cleanup");
        std::error_code ec;
        fs::remove_all(demo_root, ec);
        std::cout << "Demo files cleaned up (errors ignored)." << std::endl;

        return 0;
    }
}

int main()
{
    try
    {
        return run_demo();
    }
    catch (const std::exception &ex)
    {
        std::cerr << "Unhandled exception: " << ex.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "Unknown exception occurred." << std::endl;
    }
    return 1;
}
